﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace GDI_应用案例.Pages.Controls
{
    public unsafe class TextSelectionCode : IDisposable
    {
        private Bitmap[] _bitmaps;
        private string[] _words;
        private readonly Random _random = new Random();
        private Font _font = new Font("宋体", 40, FontStyle.Bold);// 隶书
        private int _noisePoint = 500;
        private readonly Color[] _warmColors = {
    Color.Red,      // 红色
    Color.Orange,   // 橙色
    Color.Yellow,   // 黄色
    Color.Gold,     // 金色
    Color.Coral,    // 珊瑚色
    Color.Tomato,   // 番茄红
    Color.DarkOrange, // 深橙色
    Color.Salmon,   // 鲑鱼色
    Color.IndianRed, // 印度红
    Color.Chocolate, // 巧克力色
    Color.Peru,     // 秘鲁色
    Color.Sienna,   // 赭色
    Color.DarkSalmon, // 深鲑鱼色
    Color.LightSalmon, // 浅鲑鱼色
    Color.OrangeRed, // 橙红色
    Color.DarkGoldenrod, // 深金色
    Color.Goldenrod // 金麒麟色
};
        private Dictionary<string, TextSelectionCodeResultCache> _cache = new Dictionary<string, TextSelectionCodeResultCache>();
        private Range _rotateRange = new Range(0, 360);
        private Range _distortRange = new Range(2, 5);


        public Font Font
        {
            get => _font;
            set
            {
                if (value == null) return;
                _font?.Dispose();
                _font = value;
            }
        }
        public int NoisePoint
        {
            get => _noisePoint;
            set
            {
                if (_noisePoint < 0)
                    throw new ArgumentException("噪音点不能小于零");
                _noisePoint = value;
            }
        }
        public Range RotateRange
        {
            get => _rotateRange;
            set
            {
                _rotateRange = value;
            }
        }
        public Range DistortRange
        {
            get => _distortRange;
            set
            {
                _distortRange = value;
            }
        }
        public void SetWords(string[] words)
        {
            _words = words;
        }
        public void SetImages(string[] files)
        {
            Bitmap[] bitmaps = new Bitmap[files.Length];
            fixed (String* ptr = files)
            {
                fixed (Bitmap* bptr = bitmaps)
                {
                    for (int i = 0; i < files.Length; i++)
                    {
                        if (!File.Exists(*(ptr + i)))
                            throw new FileNotFoundException("文件找不到");
                        *(bptr + i) = new Bitmap(*(ptr + i));
                    }

                }
            }
            SetImages(bitmaps);
        }
        public void SetImages(Bitmap[] bitmap)
        {
            _bitmaps = bitmap;
        }
        public bool Comparison(Point[] points, TextSelectionCodeResult codeResult)
        {
            if (points == null || points.Length == 0 || codeResult == null || !_cache.ContainsKey(codeResult.Key)) return false;

            var data = _cache[codeResult.Key];
            if (data.Length - 1 != points.Length) return false;
            Dictionary<char, int> dic = new Dictionary<char, int>();
            for (int i = 0; i < data.OriginallyWord.Length; i++)
            {
                dic.Add(data.OriginallyWord[i], i);
            }

            for (int i = 0; i < data.Word.Length; i++)
            {
                var p = points[i];
                int index = dic[data.Word[i]];
                RectangleF* ptr = data.RectPtr + index;
                if (!ptr->Contains(p))
                    return false;
            }
            data.Dispose();
            return true;
        }
        T* AllocateT<T>(int count)
        {
            int size = sizeof(T) * count;
            IntPtr memoryPtr = Marshal.AllocHGlobal(size);
            return (T*)memoryPtr;
        }
        private void FreeT<T>(T* ptr) => Marshal.FreeHGlobal((IntPtr)ptr);
        public TextSelectionCodeResult Create(string key)
        {
            if (_bitmaps == null || _bitmaps.Length == 0 || _words == null || _words.Length == 0) return new TextSelectionCodeResult();

            Bitmap bitmap = _bitmaps[_random.Next(_bitmaps.Length)].Clone() as Bitmap;
            string word = _words[_random.Next(_words.Length)];
            Graphics g = Graphics.FromImage(bitmap);
            RectangleF* rectangleFsPtr = AllocateT<RectangleF>(word.Length);
            try
            {
                fixed (char* ch = word)
                    Draw(ch, bitmap, g, Font, word.Length, rectangleFsPtr);

                string ranWord = string.Join("", word.OrderBy(c => _random.Next(word.Length)).Take(3));
                var data = new TextSelectionCodeResultCache
                {
                    Key = key,
                    OriginallyWord = word,
                    Word = ranWord,
                    RectPtr = rectangleFsPtr,
                    Length = word.Length,
                };
                if (_cache.ContainsKey(key))
                {
                    _cache[key].Dispose();
                    _cache[key] = data;
                }
                else
                    _cache.Add(key, data);
                g.Dispose();
                return new TextSelectionCodeResult
                {
                    Status = true,
                    Bitmap = bitmap,
                    Key = key,
                    Word = ranWord,
                };
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString());
                FreeT(rectangleFsPtr);
                g.Dispose();
                bitmap.Dispose();
                return new TextSelectionCodeResult
                {
                    Status = false,
                    Key = key,
                    Message = ex.Message
                };
            }
        }
        private void Draw(char* word, Bitmap bitmap, Graphics g, Font font, int wordLength, RectangleF* rectangleFsPtr)
        {
            SizeF* charSizesPtr = stackalloc SizeF[wordLength];
            for (int i = 0; i < wordLength; i++)
                *(charSizesPtr + i) = g.MeasureString((word + i)->ToString(), font);
            Point* positionsPtr = stackalloc Point[wordLength];
            GenerateNonOverlappingPositions(word, charSizesPtr, bitmap, wordLength, positionsPtr);
            for (int i = 0; i < wordLength; i++)
            {
                var color = RandomColor();
                Brush brush = new SolidBrush(color);
                *(rectangleFsPtr + i) = DrawAntiAIText(g, (word + i)->ToString(), font, brush, *(positionsPtr + i), color, *(charSizesPtr + i));
                brush.Dispose();
            }
        }
        void GenerateNonOverlappingPositions(char* word, SizeF* charSizesPtr, Bitmap bitmap, int wordLength, Point* positionsPtr)
        {
            float maxWidth = 0;
            float maxHeight = 0;
            for (int i = 0; i < wordLength; i++)
            {
                SizeF* temp = charSizesPtr + i;
                if (maxWidth < temp->Width)
                    maxWidth = temp->Width;
                if (maxHeight < temp->Height)
                    maxHeight = temp->Height;
            }
            int row = (int)(bitmap.Width / maxWidth);
            int column = (int)(bitmap.Height / maxHeight);
            int count = row * column;
            if (count < wordLength)
                throw new ArgumentException("文字太大或图像太小");

            Point* gridCellsPtr = stackalloc Point[count];
            for (int x = 0; x < row; x++)
            {
                Point* p = gridCellsPtr + x * column;
                for (int y = 0; y < column; y++)
                {
                    Point* temp = p + y;
                    temp->X = x;
                    temp->Y = y;
                }
            }
            // 两次打乱
            for (int i = 0; i < 2; i++)
                Shuffle(gridCellsPtr, count);

            int dx = (int)((bitmap.Width - maxWidth * row) / 2f), dy = (int)((bitmap.Height - maxHeight * column) / 2f);
            for (int i = 0; i < wordLength; i++)
            {
                var cell = gridCellsPtr + i;
                float cellMinX = cell->X * maxWidth;
                float cellMinY = cell->Y * maxHeight;
                Point* p = positionsPtr + i;
                p->X = (int)(cellMinX + dx);
                p->Y = (int)(cellMinY + dy);
            }
        }
        void Shuffle(Point* gridCellsPtr, int length)
        {
            int n = length;
            while (n > 1)
            {
                n--;
                int k = _random.Next(n + 1);
                Point* p1 = gridCellsPtr + k;
                Point* p2 = gridCellsPtr + n;
                (*p1, *p2) = (*p2, *p1);
            }
        }

        // 绘制抗AI识别的文字
        private RectangleF DrawAntiAIText(Graphics g, string text, Font font, Brush brush, PointF position, Color color, SizeF charSize)
        {
            // 创建 GraphicsPath 获取文字轮廓
            using (GraphicsPath path = new GraphicsPath())
            {
                // 添加文字到路径
                path.AddString(text, font.FontFamily, (int)font.Style, font.Size, position, StringFormat.GenericDefault);
                // 旋转文字
                RotateText(g, path, position, RandomInt(RotateRange), charSize); // 旋转角度
                // 扭曲文字
                DistortPath(path, RandomInt(DistortRange)); // 扭曲强度
                // 绘制阴影
                DrawTextShadow(g, path, position, color);
                // 绘制文字
                g.FillPath(brush, path);
                // 添加噪音
                //AddNoiseToText(g, path, position);

                RectangleF bounds = path.GetBounds();
                return bounds;
            }
        }
        // 绘制文字阴影
        private void DrawTextShadow(Graphics g, GraphicsPath path, PointF position, Color color)
        {
            // 创建阴影路径
            using (GraphicsPath shadowPath = (GraphicsPath)path.Clone())
            {
                // 设置阴影偏移
                Matrix shadowMatrix = new Matrix();
                shadowMatrix.Translate(_random.Next(1, 4), _random.Next(1, 4)); // 阴影偏移量
                shadowPath.Transform(shadowMatrix);

                // 绘制阴影
                using (Brush shadowBrush = new SolidBrush(Color.FromArgb(100, Color.Black)))
                {
                    g.FillPath(shadowBrush, shadowPath);
                }
            }
        }
        // 旋转文字
        private void RotateText(Graphics g, GraphicsPath path, PointF position, float angle, SizeF charSize)
        {
            // 保存当前 Graphics 状态
            Matrix originalMatrix = g.Transform;
            // 设置旋转中心点
            float x = position.X + charSize.Width / 2f, y = position.Y + charSize.Height / 2f;
            g.TranslateTransform(x, y);
            g.RotateTransform(angle); // 旋转角度
            g.TranslateTransform(-x, -y);
            // 应用旋转
            path.Transform(g.Transform);
            // 恢复 Graphics 状态
            g.Transform = originalMatrix;
            originalMatrix.Dispose();
        }
        // 扭曲文字路径
        private void DistortPath(GraphicsPath path, float intensity)
        {
            PointF[] points = path.PathPoints;
            fixed (PointF* pf = points)
            {
                for (int i = 0; i < points.Length; i++)
                {
                    PointF* p = pf + i;
                    // 对每个点进行随机偏移
                    p->X += (float)(_random.NextDouble() - 0.5) * intensity;
                    p->Y += (float)(_random.NextDouble() - 0.5) * intensity;
                }
            }

            // 更新路径
            path.Reset();
            path.AddLines(points);
        }
        private void AddNoiseToText(Graphics g, GraphicsPath path, PointF position)
        {
            RectangleF bounds = path.GetBounds();

            List<PointF> points = new List<PointF>(NoisePoint);
            List<SolidBrush> sbs = new List<SolidBrush>(NoisePoint);

            for (int i = 0; i < NoisePoint; i++)
            {
                float x = bounds.Left + (float)_random.NextDouble() * bounds.Width;
                float y = bounds.Top + (float)_random.NextDouble() * bounds.Height;
                if (path.IsVisible(x, y))
                {
                    points.Add(new PointF(x, y));
                    sbs.Add(new SolidBrush(RandomColor()));
                }
            }
            for (int i = 0; i < points.Count; i++)
            {
                var sb = sbs[i];
                var p = points[i];
                float x = p.X;
                float y = p.Y;
                g.FillRectangle(sb, x + position.X, y + position.Y, 1, 1);
                sb.Dispose();
            }
        }

        protected Brush CreateLinearGradientBrush(RectangleF rectangle)
        => new LinearGradientBrush(rectangle, _warmColors[_random.Next(_warmColors.Length)], _warmColors[_random.Next(_warmColors.Length)], RandomMode);
        protected LinearGradientMode RandomMode => (LinearGradientMode)_random.Next(4);
        protected Color RandomColor(int alpha = 255)
            => Color.FromArgb(alpha, _random.Next(256), _random.Next(256), _random.Next(256));
        protected int RandomInt(Range range) => _random.Next(range.MinValue, range.MaxValue);
        public void Dispose()
        {
            if (_bitmaps != null)
            {
                fixed (Bitmap* bptr = _bitmaps)
                {
                    for (int i = 0; i < _bitmaps.Length; i++)
                    {
                        (bptr + i)->Dispose();
                    }
                }
            }

            foreach (var item in _cache)
            {
                item.Value.Dispose();
            }
        }
        ~TextSelectionCode()
        {
            Dispose();
        }
    }
    public class TextSelectionCodeResult
    {
        public Bitmap Bitmap { get; set; }
        public string Word { get; set; }
        public string Key { get; set; }
        public bool Status {  get; set; }
        public string Message {  get; set; }
    }
    public unsafe class TextSelectionCodeResultCache : TextSelectionCodeResult, IDisposable
    {
        private RectangleF* _ptr;
        public RectangleF* RectPtr
        {
            get { return _ptr; }
            set { _ptr = value; }
        }
        public int Length { get; set; }
        public string OriginallyWord { get; set; }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void FreeT<T>(T* ptr) => Marshal.FreeHGlobal((IntPtr)ptr);
        private bool _disposed;
        private object _obj = new object();
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;
            lock (_obj)
            {
                if (_disposed) return;
                if (disposing)
                {
                    // 托管资源
                }

                // 非托管资源
                FreeT(RectPtr);
                _disposed = true;
            }
        }
        ~TextSelectionCodeResultCache()
        {
            Dispose(false);
        }
    }
    public struct Range
    {
        public Range(int minValue, int maxValue)
        {
            this.MinValue = minValue;
            this.MaxValue = maxValue;
        }

        public int MinValue { get; set; }
        public int MaxValue { get; set; }
    }
}
